package com.scse.bms.web.client;

import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.scse.bms.domain.*;
import com.scse.bms.entity.*;
import com.scse.bms.exception.CommonException;
import com.scse.bms.service.*;
import com.scse.bms.util.HistoryUtil;
import com.scse.bms.util.VerifyImgCodeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @Title: GenController
 * @Author: 吴赠禹
 * @Date: 2020-11-12 下午 5:53
 * @Version: 1.0
 * @Description: 通用处理器
 */

@Slf4j
@Controller
public class CGenController {

    @Autowired
    private UserService userService;

    @Autowired
    private BookService bookService;

    @Autowired
    private PressService pressService;

    @Autowired
    private ThemeService themeService;

    @Autowired
    private DownloadService downloadService;

    @Autowired
    private HeadImgService headImgService;


    @Autowired
    private CommentService commentService;

    /**
     * 请求生成验证码
     */
    @GetMapping("/randomCode")
    public void flashCode(HttpServletRequest request, HttpServletResponse response) {
        try {
            try {
                // 获取session并清除过时验证码
                HttpSession session = request.getSession();
                session.removeAttribute("code");
                String word = VerifyImgCodeUtil.generateImgCode(response);
                // 设置session
                session.setAttribute("code", word.toLowerCase());
            } catch (IOException e) {
                throw new CommonException("error", e);
            }
        } catch (CommonException e) {
            log.error("[CommonException] flashCode 生成验证码失败!\n" + e.getMessage(), e);
        }
    }




    /**
     * TODO 用户登录，逻辑过于复杂
     * 2021-2-25 UserVO兼容UserDO（多了个角色字段，统一保存为UserVO）
     * @param map
     * @param request
     * @return
     */
    @PostMapping("/user/login")
    @ResponseBody
    public String login(@RequestBody Map<String, String> map, HttpServletRequest request) {
        // 使用map接收参数进行对比，调用mapper接口查找用户
        String account = map.get("account");
        String password = map.get("password");
        log.info("[login] 用户请求登录：{}", account);
        JSONObject bean = new JSONObject();
        // 比较验证码
        String code = (String) request.getSession().getAttribute("code");
        // 验证码不一致，返回错误提示
        if (!code.equalsIgnoreCase(map.get("randomCode"))) {
            bean.put("code", 1003);
            return bean.toJSONString();
        }
        // 调用service接口方法，内部已实现根据用户名获账号查找
        UserVO user = userService.findUserToLogin(account, DigestUtil.md5Hex(password));
        if (user == null) {
            bean.put("code", 1004);
            return bean.toJSONString();
        }
        // 账号被禁用
        final int disabled = 0;
        if (user.getValid() == disabled) {
            bean.put("code", disabled);
            return bean.toJSONString();
        }
        // UserHeadImgVO headImg = headImgService.getHeadImgVOByUserId(user.getId());
        // 保存session
        HttpSession session = request.getSession();
        // 判断用户是否访问了限制URI

        log.info("[login] 用户:{} 登录成功，保存session。loginUser:{}", account, user);
        session.setAttribute("loginUser", user);
        bean.put("code", 200);
        if (session.getAttribute("url") != null) {
            bean.put("url", session.getAttribute("url"));
        }
        return bean.toJSONString();
    }


    /**
     * 退出登录
     *
     * @param session
     * @return
     */
    @GetMapping("/user/logout")
    public String logout(HttpSession session) {
        session.removeAttribute("loginUser");
        if (session.getAttribute("url") != null) {
            session.removeAttribute("url");
        }
        return "redirect:/index";
    }


    /**
     * 访问首页，只有后台对主题分类的图书才显示
     * @param model
     * @return
     */
    @GetMapping(value = {"/","/index", "/index.html"})
    public String toIndexPage(Model model) {

        // 获取编者推荐图书（管理员推荐）
        List<BookVO> commendList = bookService.getBookWithRecommend(10);
        model.addAttribute("commendList", commendList);
        
        // 获取新书速递图书（关键字段uploadTime，只有pdf存在财有该字段，按时间降序）
        List<BookVO> recentList = bookService.getBookWithRecently(10);
        model.addAttribute("recentList", recentList);

        // 获取作者信息（有作者信息的图书）
        List<BookVO> authorList = bookService.showAuthors(6);
        model.addAttribute("authorList", authorList);

        // 获取出版社信息
        List<PressDO> pressList = pressService.showPress(9);
        model.addAttribute("pressList", pressList);

        // 获取导航菜单
        List<MenuItem> itemList = themeService.getTheme();
        model.addAttribute("itemList", itemList);

        return "index";
    }


    /**
     * 去主题列表页
     * @param id
     * @param pn
     * @param model
     * @return
     */
    @GetMapping("/theme/{id}")
    public String toThemePage(@PathVariable("id") Integer id,
                              @RequestParam(value="pn", defaultValue = "1") Integer pn,
                              Model model) {
        // 获取导航菜单
        List<MenuItem> itemList = themeService.getTheme();
        model.addAttribute("itemList", itemList);
        // 根据主题ID获取名称（用于前端显示位置）
        ThemeVO theme = themeService.getById(id);
        // 获取主题类的书集合
        Page<BookDO> bookPage = new Page<>(pn, 10);
        IPage<BookDO> page = bookService.selectBookPage(bookPage, id);
        model.addAttribute("bookList", page);
        // 记录当前请求的主题id和名称
        model.addAttribute("themeId", id);
        model.addAttribute("themeName", theme.getName());
        return "theme";
    }


    /**
     * 图书详情页 TODO 为了防止主题为空，后台需要修改默认为其它类型（不指定主题时的类型）
     * 图书详情页响应巨慢！！
     * 2021-2-25 已修改默认值
     * 2020-3-5 400 错误是因为模板问题，在detail页面引用了common页面，用户输入关键词，url成了detail?keyword=...
     * 可以先对ID判断，如果不存在则跳转index页面。TODO 后记，rebuild之后莫名其妙就好了。。。
     * @param id
     * @param model
     * @param session
     * @return
     */
    @GetMapping("/detail")
    public String toDetailPage(@RequestParam(value= "id") Integer id, Model model, HttpSession session) {
        // 获取导航菜单
        List<MenuItem> itemList = themeService.getTheme();
        model.addAttribute("itemList", itemList);

        // 获取图书信息
        BookDO book = bookService.selectBook(id);
        model.addAttribute("book", book);

        // 根据图书ID获取主题名（可能为空） TODO 首页、查询页限制（主题未不显示，数据库中0表示无主题，即管理员未审核）
        ThemeVO theme = themeService.getById(book.getThemeId());
        model.addAttribute("theme", theme);

        // 记录浏览历史（注意判空）
        UserVO user = (UserVO) session.getAttribute("loginUser");
        HistoryDO history = HistoryUtil.generateHistory(user, id, theme);
        bookService.saveHistory(history);

        // 最新上架推荐（最多能取15条，默认6条）（新书推荐） TODO 不显示未分类的图书
        List<BookVO> newList = bookService.getNewBookWithUploadTimeAndDisorder(6);
        model.addAttribute("newList", newList);

        // 相关推荐（同一主题）
        List<BookVO> sameList = bookService.getNewBookWithThemeId(theme.getId(), 6);
        model.addAttribute("sameList", sameList);

        // 访问最多图书
        List<BookVO> mostVisitedList = bookService.getBookWhichMostVisited(6);
        model.addAttribute("visitList", mostVisitedList);

        // 下载最多图书
        List<BookVO> downloadList = bookService.getBookWhichMostDownload(6);
        model.addAttribute("downList", downloadList);

        // 获取评论列表
        List<CommentVO> commentList = commentService.listForUserScan(id);
        model.addAttribute("commentList", commentList);

        return "detail";
    }


    /**
     * 去下载页面（只有登录的用户才能访问，默认拦截）
     * @param id
     * @param model
     * @return
     */
    @GetMapping("/toDownload")
    public String toDownloadPage(@RequestParam("id") Integer id, Model model) {
        // 获取图书信息
        BookDO book = bookService.selectBook(id);
        model.addAttribute("book", book);
        return "download";
    }


    /**
     * 下载PDF TODO 需要记录主题ID
     * @param booId
     * @return
     */
    @GetMapping("/download/{themeId}/{bookId}")
    public ResponseEntity<byte[]> download(@PathVariable("themeId") Integer themeId,
                                           @PathVariable("bookId") Integer booId,
                                           @RequestParam("fileName") String fileName,
                                           HttpSession session, HttpServletRequest request) throws Exception {
        UserVO user = (UserVO) session.getAttribute("loginUser");
        log.info("[download] 用户：{} 请求下载：{}", user.getId(), fileName);
        // 记录用户下载行为
        downloadService.save(user.getId(), booId, themeId);
        BookDO book = bookService.selectBook(booId);
        // 获取文件保存路径
        String path = book.getSrc();
        // 创建文件对象
        File file = new File(path);
        // 对文件名转码（解决中文乱码）
        fileName = getFileName(request, fileName);
        // 设置响应头
        HttpHeaders headers = new HttpHeaders();
        // 通知浏览器以下载方式打开
        headers.setContentDispositionFormData("attachment", fileName);
        // 定义以流的形式下载返回文件数据（这一行代码没有影响）
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        try {
            return new ResponseEntity<>(FileUtils.readFileToByteArray(file), headers, HttpStatus.OK);
        } catch (IOException e) {
            log.info("[download] 异常：{}" , e.getMessage());
            return new ResponseEntity<byte[]>(e.getMessage().getBytes(), HttpStatus.EXPECTATION_FAILED);
        }
    }


    /**
     * 根据浏览器的差异进行编码设置，返回编码后的文件名
     * @param request
     * @param fileName
     * @return
     */
    private String getFileName(HttpServletRequest request, String fileName) throws IOException {
        // IE不同版本User-Agent中出现的关键词
        String[] ieKeywords = {"MSIE", "Trident", "Edge"};
        // 获取请求头代理信息
        String userAgent = request.getHeader("User-Agent");
        for (String keyword : ieKeywords) {
            // IE 内核浏览器，统一为UTF-8编码显示，并对转换的+进行更正
            if (userAgent.contains(keyword)) {
                return URLEncoder.encode(fileName, "UTF-8").replace("+", "");
            }
        }
        // 火狐等其它浏览器统一为ISO-8859-1编码显示
        return new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
    }


    /**
     * 获取当日下载次数
     * @param session
     * @return
     */
    @GetMapping("/downloadHistory/today/get")
    @ResponseBody
    public String getTodayDownCount(HttpSession session) {
        UserVO user = (UserVO) session.getAttribute("loginUser");
        Integer count = downloadService.getDownCountByToday(user.getId());
        if (null == count) {
            count = 0;
        }
        return String.valueOf(count);
    }


    /**
     * 查询页面（未进行主题分类的不予显示）
     * @param keyword
     * @param pn
     * @param model
     * @return
     */
    @GetMapping("/search")
    public String toSearch(@RequestParam("keyword") String keyword,
                           @RequestParam(value = "pn", defaultValue = "1") Integer pn,
                           Model model) {
        log.info("[toSearch] 用户请求搜索资源，输入的关键词是：" + keyword);
        // 获取导航菜单
        List<MenuItem> itemList = themeService.getTheme();
        model.addAttribute("itemList", itemList);
        // 获取模糊查询结果
        Page<BookVO> bookPage = new Page<>(pn, 10);
        IPage<BookVO> page = bookService.selectBookPageByTitleOrAuthor(bookPage, keyword);
        model.addAttribute("bookList", page);
        // 记录本次请求的关键字（用于下次分页请求）
        model.addAttribute("keyword", keyword);
        return "search";
    }


    /**
     * 预览PDF 记录浏览历史
     * @param booId
     * @return
     */
    @GetMapping("/preview/{themeId}/{bookId}")
    public void preview(@PathVariable("themeId") Integer themeId,
                                          @PathVariable("bookId") Integer booId,
                                          @RequestParam("fileName") String fileName,
                                           HttpSession session, HttpServletRequest request,
                                            HttpServletResponse response) throws Exception {
        UserVO user = (UserVO) session.getAttribute("loginUser");
        log.info("[preview] 用户：{} 请求预览：{}", user.getId(), fileName);
        // 记录用户浏览行为和下载行为 TODO 因为目前没有对预览进行控制，客户端是可以下载的
        bookService.saveHistory2(user.getId(), booId, themeId);
        downloadService.save(user.getId(), booId, themeId);
        BookDO book = bookService.selectBook(booId);
        // 获取文件保存路径
        String path = book.getSrc();
        log.info("[preview] fileName:{}, path:{}", fileName, path);
        // 创建文件对象
        File file = new File(path);
        // 对文件名转码（解决中文乱码）
        fileName = getFileName(request, fileName);
        // 设置响应头和内容类型
        response.setContentType("application/pdf");
        response.setHeader("Content-Disposition", "inline;fileName=" + fileName);
        // 通过文件流读取文件
        InputStream in = new FileInputStream(file);
        // 获取response对象的输出流
        OutputStream out = response.getOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        // 循环取出流中的数据
        while ((len = in.read(buffer)) != -1) {
            out.write(buffer, 0, len);
        }

    }

}





